/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package service

import (
	"did-service/bean/entity"
	"did-service/bean/model"
	model2 "did-service/chain/model"
	"did-service/common/constant"
	"did-service/common/db"
	"did-service/common/uuid"
	"did-service/config"

	"github.com/pkg/errors"
)

func (srv *DidService) CreateDid(walletId string) (*entity.DidDocument, error) {
	document, err := srv.ApplyDidDocument()
	if err != nil {
		log.Errorf("apply did document error: %s", err)
		return nil, err
	}
	createdTime, _ := parseTime(document.Created)
	updatedTime, _ := parseTime(document.Updated)
	documentTable := &entity.DidDocument{
		Id:             uuid.Generate(),
		Did:            document.Id,
		WalletId:       walletId,
		ProofCreator:   document.Proof.Creator,
		ProofType:      constant.DefaultKeyAlgoCode,
		ProofSignature: document.Proof.SignatureValue,
		Status:         "1",
		Note:           "",
		Version:        document.Version,
		AuthPubKey:     document.Authentication[0],
		RecoveryPubKey: document.Recovery[0],
		CreatedTime:    createdTime,
		UpdatedTime:    updatedTime,
	}
	_, err = srv.engine.InsertOne(documentTable)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	didInfo := model2.DidInfo{
		Did:             document.Id,
		WalletId:        walletId,
		Version:         document.Version,
		Authentication:  document.PublicKey[0].PublicKeyHex,
		Recovery:        document.PublicKey[1].PublicKeyHex,
		ResolveEndpoint: srv.getDidResolveUrl(),
		ProofSignature:  document.Proof.SignatureValue,
	}
	go func(id string, didInfo *model2.DidInfo) {
		txId, _ := srv.getChainClient().SaveDidInfo(didInfo)
		didDocument := entity.DidDocument{
			TxId: txId,
		}
		_, err := srv.engine.Where("id = ?", id).Cols("tx_id").Update(&didDocument)
		if err != nil {
			log.Errorf("updata did txid error: %v", err)
		}
	}(documentTable.Id, &didInfo)

	return documentTable, nil
}

func (srv *DidService) getDidResolveUrl() string {
	endpoint := config.Get("did.endpoint", constant.DefaultServiceResolveEndpoint)
	return endpoint + constant.DidResolveUrlSuffix
}

func (srv *DidService) GetDidDocument(id string) (*entity.DidDocument, error) {
	var document entity.DidDocument
	ok, err := srv.engine.Where("id = ?", id).Get(&document)
	if err != nil {
		log.Errorf("get did document error: %v", err)
		return nil, err
	}
	if !ok {
		log.Warning("did document not exist, id[%s]", id)
		return nil, nil
	}
	return &document, nil
}

func (srv *DidService) DeleteDid(id string) error {
	didDocument, err := srv.GetDidDocument(id)
	if err != nil {
		log.Errorf("delete did error: %v", err)
		return err
	}
	if didDocument == nil {
		log.Warning("did not exist")
		return errors.New("did not exist")
	}

	issuer, err := srv.GetIssuerByDid(didDocument.Did)
	if err != nil {
		log.Errorf("delete did error, get issuer by did error: %v", err)
		return err
	}

	if issuer != nil && issuer.Status != constant.StatusDeactivate {
		return constant.ErrorDidUsed
	}

	document := entity.DidDocument{
		Status: "0",
	}
	_, err = srv.engine.Where("id = ?", id).Cols("status").Update(&document)
	if err != nil {
		log.Errorf("update document status error: %v", err)
		return err
	}
	return nil
}

func (srv *DidService) ListKeyByDid(did string) ([]*entity.DidDocumentKey, error) {
	var keys []*entity.DidDocumentKey
	err := srv.engine.Where("did = ?", did).Find(&keys)
	if err != nil {
		log.Errorf("query did keys from db error: %v", err)
		return nil, err
	}
	return keys, nil
}

// GetDidListByWalletId 获取wallet下的所有did
func (ds *DidService) GetDidListByWalletId(walletId string) ([]model.DidBaseInfo, error) {
	var (
		find = make([]entity.DidDocument, 0)
		ret  []model.DidBaseInfo
	)
	if err := ds.engine.Where("wallet_id = ? and status = ?", walletId, constant.DidValidStatus).
		Cols("did", "id").
		Find(&find); err != nil {
		return nil, err
	}
	ret = make([]model.DidBaseInfo, 0, len(find))
	for _, doc := range find {
		ret = append(ret, model.DidBaseInfo{
			Id:  doc.Id,
			Did: doc.Did,
		})
	}
	return ret, nil
}

var (
	_didPageCols = []string{"did", "id", "version", "created_time", "updated_time"}
)

func (ds *DidService) didEntityConvPageInfo(origin *entity.DidDocument) model.DidPageInfo {
	if origin == nil {
		return model.DidPageInfo{}
	}
	return model.DidPageInfo{
		Id:         origin.Id,
		Did:        origin.Did,
		Version:    origin.Version,
		CreateTime: origin.CreatedTime.Format(constant.TimeLayout),
		UpdateTime: origin.UpdatedTime.Format(constant.TimeLayout),
	}
}

// GetDidSpPage 获取did指定页表数
func (ds *DidService) GetDidSpPage(walletId, did string, pageNo, pageSize int) (*db.Page, error) {
	var (
		res  []model.DidPageInfo
		err  error
		find = &entity.DidDocument{}
		ok   bool
	)
	if ok, err = ds.engine.Where("wallet_id = ? and did = ? and status = ?",
		walletId, did, constant.DidValidStatus).
		Cols(_didPageCols...).Get(find); err != nil {
		return nil, err
	}
	if ok {
		res = append(res, ds.didEntityConvPageInfo(find))
	}
	total := int64(len(res))
	return db.OnePage(pageNo, pageSize, total, res), err
}

// GetDidPage 获取did分页数据
func (ds *DidService) GetDidPage(walletId string, pageNo, pageSize int) (*db.Page, error) {
	total, err := ds.engine.Where("wallet_id = ? and status = ?", walletId, constant.DidValidStatus).
		Count(&entity.DidDocument{})
	if err != nil {
		return nil, err
	}
	var (
		didList = make([]*entity.DidDocument, 0)
	)
	page := db.OnePage(pageNo, pageSize, total, nil)
	if !page.OutBorder() {
		if err = ds.engine.Where("wallet_id = ? and status = ?", walletId, constant.DidValidStatus).
			Cols(_didPageCols...).Limit(page.OrmLimit()).
			Desc("updated_time").Find(&didList); err != nil {
			return nil, err
		}
		var ret []model.DidPageInfo
		for _, didInfo := range didList {
			ret = append(ret, ds.didEntityConvPageInfo(didInfo))
		}
		page.CreatePage(ret)
	}
	return page, nil
}
